using System;
using System.Collections.Generic;
using System.Text;
using ElteIk.RoboSoccer.Core;
using ElteIk.RoboSoccer.Core.Physics;

namespace ElteIk.RoboSoccer.Simulator
{
    #region StartingState Class

    /// <summary>
    /// Class for setup and generate the field's starting state.
    /// </summary>
    public class StartingState
    {
        private BallState ballState;
        public BallState BallState { get { return ballState; } }

        private TeamState leftTeamState;
        public TeamState LeftTeamState { get { return leftTeamState; }  }

        private TeamState rightTeamState;
        public TeamState RightTeamState { get { return rightTeamState; } }

        private static Random random;

        public StartingState(int playersPerTeam) 
        {
            random = new Random();
            ballState = new BallState();
            
            leftTeamState = new TeamState(1,playersPerTeam);
            rightTeamState = new TeamState(2,playersPerTeam);
        }

        // In default case a team has 3 players.
        public StartingState()
            : this(3) { }

        #region SetBallState functions

        /// <summary>
        /// Set the ball's position to a point of the circle with center = position and maximum radius = maxDistance.
        /// Generate the ball's velocity with direction between minAngle and maxAngle (0-360) also with power between minVelocity and maxVelocity (0-).
        /// </summary>
        /// <param name="position"></param>
        /// <param name="maxDistance"></param>
        /// <param name="minAngle"></param>
        /// <param name="maxAngle"></param>
        /// <param name="minVelocity"></param>
        /// <param name="MaxVelocity"></param>
        public void SetBallState(Vector2D position, float maxDistance, float minAngle, float maxAngle, float minVelocity, float maxVelocity)
        {
            ballState.Position = GeneratePoint(position, maxDistance);
            float power = GenerateFloatBetweenNumbers(maxVelocity, minVelocity);
            float angle = GenerateFloatBetweenNumbers(maxAngle, minAngle);
            ballState.Velocity = Vector2D.DegreesToNormalizedVector(angle) * power;
        }

        /// <summary>
        /// Set the ball's position to a point of the circle with center = position and maximum radius = maxDistance.
        /// Set the ball's velocity with direction angle (0-360) also with power velocity (0-).
        /// </summary>
        /// <param name="position"></param>
        /// <param name="maxDistance"></param>
        /// <param name="angle"></param>
        /// <param name="velocity"></param>
        public void SetBallState(Vector2D position, float maxDistance, float angle, float velocity)
        {
            SetBallState(position,maxDistance,angle,angle,velocity,velocity);
        }

        /// <summary>
        /// Set the ball's position to a point of the circle with center = position and maximum radius = maxDistance.
        /// </summary>
        /// <param name="position"></param>
        /// <param name="maxDistance"></param>
        public void SetBallState(Vector2D position, float maxDistance)
        {
            SetBallState(position,maxDistance,0,0,0,0);
        }

        #endregion

        #region Static functions for generating

        public static Vector2D GeneratePoint(Vector2D center, float maxDistance)
        {
            float angle = random.Next(360);
            return (Vector2D.DegreesToNormalizedVector(angle)*maxDistance) + center;
        }

        public static float GenerateFloatBetweenNumbers(float min, float max)
        {
            return (float)(random.NextDouble() * (max - min)) + min;
        }

        #endregion
    }
    #endregion

    #region PhysicalObjectState Class

    /// <summary>
    /// Base class for a PhysicalObject's starting state
    /// </summary>
    public abstract class PhysicalObjectState
    {
        protected Boolean isSet;
        public Boolean IsSet { get { return isSet; } }
        
        protected Vector2D position;
        public Vector2D Position { get { return position; } set { position = value; isSet = true; } }

        protected PhysicalObjectState()
        {
            position = Vector2D.NullVector;
            isSet = false;
        }
    }
    #endregion

    #region BallState Class & PlayerState Class

    public class BallState : PhysicalObjectState
    {
        private Vector2D velocity;
        public Vector2D Velocity { get { return velocity; } set { velocity = value; isSet = true; } }

        public BallState()
            : base()
        {
            velocity = Vector2D.NullVector;
        }
    }


    public class PlayerState : PhysicalObjectState
    {
        private Vector2D heading;
        public Vector2D Heading { get { return heading; } set { heading = value; isSet = true; } }

        public PlayerState(Vector2D heading)
            : base()
        {
            this.heading = heading;
        }
    }

    #endregion

    #region TeamState Class

    public class TeamState
    {
        private int teamId;
        
        private List<PlayerState> playerStateList;
        public List<PlayerState> PlayerStateList { get { return playerStateList; } }

        public TeamState(int teamId, int playerNr)
        {
            this.teamId = teamId;
            playerStateList = new List<PlayerState>();
            Vector2D heading = (teamId == 1) ? Vector2D.X_Axis : Vector2D.X_Axis.Negated;
            for (int i = 0; i < playerNr; ++i)
            {
                playerStateList.Add(new PlayerState(heading));
            }
        }

        /// <summary>
        /// Set the player's position [playerId = (0..playersPerTeam-1)] to a point of the circle with center = position and maximum radius = maxDistance also it's heading between minAngle and maxAngle (0-360.)
        /// </summary>
        /// <param name="playerId"></param>
        /// <param name="position"></param>
        /// <param name="maxDistance"></param>
        /// <param name="minAngle"></param>
        /// <param name="maxAngle"></param>
        public void SetPlayerState(int playerId, Vector2D position, float maxDistance, float minAngle, float maxAngle)
        {
            // set the state just for valid players 
            if (playerId >= 0 && playerId < playerStateList.Count)
            {
                playerStateList[playerId].Position = StartingState.GeneratePoint(position,maxDistance);
                playerStateList[playerId].Heading = Vector2D.DegreesToNormalizedVector(StartingState.GenerateFloatBetweenNumbers(minAngle,maxAngle));
            }
        }

        /// <summary>
        /// Set the player's position [playerId = (0..playersPerTeam-1)] to a point of the circle with center = position and maximum radius = maxDistance also it's heading to angle (0-360.)
        /// </summary>
        /// <param name="playerId"></param>
        /// <param name="position"></param>
        /// <param name="maxDistance"></param>
        /// <param name="angle"></param>
        public void SetPlayerState(int playerId, Vector2D position, float maxDistance, float angle)
        {
            SetPlayerState(playerId, position, maxDistance, angle, angle);
        }

        /// <summary>
        /// Set the player's position [playerId = (0..playersPerTeam-1)] to a point of the circle with center = position and maximum radius = maxDistance.
        /// </summary>
        /// <param name="playerId"></param>
        /// <param name="position"></param>
        /// <param name="maxDistance"></param>
        public void SetPlayerState(int playerId, Vector2D position, float maxDistance)
        {
            // set the state just for valid players 
            if (playerId >= 0 && playerId < playerStateList.Count)
            {
                playerStateList[playerId].Position = StartingState.GeneratePoint(position, maxDistance);
            }            
        }
    }

    #endregion

}
